前段时间,我急需这种模块化的方法来管理我的JavaScript代码
Javascript模块化编程这一系列文章给我很大帮助,介绍得很通俗易懂,小伙伴们可以看看,我将在此文基础上记录一些我的笔记。
JavaScript模块简介
JavaScript不是一种模块化编程语言,它不支持“类”(class),更不用说“模块”(module)了。(正在制定中的ECMAScript标准第六版,将正式支持”类”和”模块”,但还需要很长时间才能投入实用。)
我们可以使用函数和闭包来构造模块。模块是一个提供接口却隐藏状态与实现的函数或对象。通过使用函数产生模块,我们几乎可以完全摒弃全局变量的使用,从而缓解这个JavaScript的最为糟糕的特性之一所带来的影响。
JavaScript模块写法
立即执行函数
1 2 3 4 5 6 7 8 9 10 11 12 13
| var bar = (function (){ var count=0; var setCount = function(start){ count=start; } var getCount = function(){ return count; } return { setCount:setCount, getCount:getCount, } })();
|
本例是JavaScript模块的基本写法。使用到了闭包。
此时外部代码无法读取内部的count变量。
1
| console.log(foo.count); //undefined
|
导入全局变量
把全局变量作为参数传递给一个立即执行函数,这样就完成了全局变量的导入,立即执行函数中可以使用此全局变量的方法,并可以修改(简化)全局变量的名称
1 2 3
| (function ($, YAHOO) { // now have access to globals jQuery (as $) and YAHOO in this code }(jQuery, YAHOO))
|
导出模块
通过在立即执行函数中返回一个Object,将模块导出到全局空间供其他模块使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| var foo = (function () { var mod = {}, privateVariable = 1; function privateMethod() { console.log("this is a private method"); } mod.moduleProperty = 2; mod.moduleMethod = function () { console.log(privateVariable); }; mod.callPrivateMethod = function(){ privateMethod(); } return mod; }()); foo.moduleMethod(); console.log(foo.moduleProperty); foo.callPrivateMethod(); foo.privateMethod(); console.log(foo.privateVariable);
|
方法的重写
在“导出模块”例子基础上,重写他的moduleMethod方法..
1 2 3 4 5 6 7 8 9 10 11
| var foo = (function (mod) { var old_moduleMethod = mod.moduleMethod; mod.moduleMethod = function () { // method override, has access to old through old_moduleMethod... console.log("this is already overridden"); }; return mod; }(foo)); //test foo.moduleMethod(); //output:"this is already overridden"
|
并且你可以在新方法中访问老方法,如果需要的话。
克隆并重写方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| var cfoo = (function(old){ var mod = {}, key; for (key in old) { if (old.hasOwnProperty(key)) { mod[key] = old[key]; } } var super_moduleMethod = old.moduleMethod; mod.moduleMethod = function () { console.log("this is a override method on the clone"); }; return mod; })(foo); foo.moduleMethod(); cfoo.moduleMethod(); cfoo.callPrivateMethod();
|
此可以克隆某个模块并可根据自己需要重写其方法。
模块的继承
如果一个模块很大,必须分成几个部分,或者一个模块需要继承另一个模块。在”立即执行函数”例子的基础上,继承bar。
1 2 3 4 5 6 7 8 9 10 11 12
| var bar = (function(mod){ mod.newFunc=function(){ console.log("this is a new function"); } return mod; })(bar); console.log(bar.getCount()); bar.setCount(4); console.log(bar.getCount()); bar.newFunc();
|
我们看到可以访问foo中的老函数和新方法。那他可不可以访问foo中的私有变量呢?
let’s have a try…
1 2 3 4 5 6 7 8 9 10 11 12 13
| var bar = (function(mod){ mod.newFunc=function(){ console.log("this is a new function"); } mod.testFunc=function(){ console.log(count); console.log(this.count); } return mod; })(bar); bar.testFunc();
|
注意:这种方法是访问不到父模块的私有变量的。
在浏览器环境中,模块的各个部分通常都是从网上获取的,有时无法知道哪个部分会先加载。如果采用上一节的写法,第一个执行的部分有可能加载一个不存在空对象。可以用一下方式解决:
1 2 3 4 5 6
| var bar = (function(mod){ mod.newFunc=function(){ console.log("this is a new function"); } return mod; })(bar||{});
|
模块应用-单例模式
模块模式通常结合单例模式(Singleton Pattern)使用。JavaScript的单例就是用对象字面量表示法创建的对象,对象的属性值可以试数值或函数,并且属性值在该对象的生命周期中不会发生变化。
单例的最佳实践:
1 2 3 4 5 6 7 8 9 10 11 12 13
| var Universe; (function () { var instance; Universe = function Universe() { if (instance) { return instance; } instance = this; this.start_time = 0; this.bang = "Big"; }; }());
|
测试一下一以上代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| var uni = new Universe(); var uni2 = new Universe(); console.log(uni===uni2); Universe.prototype.nothing = true; var uni = new Universe(); Universe.prototype.everything = true; var uni2 = new Universe(); console.log(uni.nothing); console.log(uni2.nothing); console.log(uni.everything); console.log(uni2.everything); console.log(uni.constructor.name); console.log(uni.constructor === Universe);
|
我们发现new出来的两个Universe对象是同一个所以他们是同一个对象,实现了单例,且加进原型的属性在两个对象均可访问。
关于constructor:
constructor属性不影响任何JavaScript的内部属性。instanceof检测对象的原型链,通常你是无法修改的(不过某些引擎通过私有的proto属性暴露出来)。
constructor其实没有什么用处,只是JavaScript语言设计的历史遗留物。由于constructor属性是可以变更的,所以未必真的指向对象的构造函数,只是一个提示。不过,从编程习惯上,我们应该尽量让对象的constructor指向其构造函数,以维持这个惯例。
模块的规范
目前,通行的Javascript模块规范有:CommonJS(node.js应用此规范),AMD(异步模块定义),CMD(通用模块定义)。
主要有两个Javascript库实现了AMD规范:require.js和curl.js。
实现了CMD规范:sea.js。
关于这些库和规范的讨论相关文章有:
seaJS与RequireJS的异同
AMD 和 CMD 的区别有哪些?
先写到这,对于这些库,尚在了解中。等应用一阵再写相关的博文吧。
参考文献:
[1] Javascript模块化编程
[2] JavaScript Module Pattern: In-Depth
[3] JavaScript - The Good Parts
[4] 深入理解JavaScript系列(25):设计模式之单例模式
[5] javascript-patterns singleton